`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file UticaCPUDecodeStage.v
	@author Andrew D. Zonenberg
	@brief Decode stage of the CPU
 */
module UticaCPUDecodeStage(
	clk, hold,
	ibus_din, ibus_dinok,
	decode_pc, decode_bubble,
	stall, writeback_cachemiss,
	decode_rsid, decode_rtid,
	execute_div, execute_divu, 
	execute_beq, execute_bne, execute_bgtz, execute_blez, execute_bgez, execute_bgezal, execute_bltz, execute_bltzal,
	execute_j, execute_jr, execute_jal, execute_jalr, execute_mfhi, execute_mflo,
	execute_imm_sx, execute_imm_zx, execute_rdid, execute_regwrite, execute_imm,
	execute_jump_raw, execute_shamt, execute_rtype, execute_func, execute_rsid, execute_rtid, execute_opcode,
	execute_bubble, execute_uses_rs, execute_uses_rt,
	decode_cp0_regid, decode_mispredict
	);
	 
	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	input wire clk;
	input wire hold;
	
	input wire[31:0] ibus_din;
	input wire ibus_dinok;
	
	input wire[31:0] decode_pc;
	input wire decode_bubble;
	
	input wire stall;
	
	input wire writeback_cachemiss;
	
	output wire[4:0] decode_rsid;
	output wire[4:0] decode_rtid;
	
	//One-hot enables for each instruction
	output reg execute_beq = 0;
	output reg execute_bne = 0;
	output reg execute_bgtz = 0;
	output reg execute_blez = 0;
	output reg execute_bgez = 0;
	output reg execute_bgezal = 0;
	output reg execute_bltz = 0;
	output reg execute_bltzal = 0;
	output reg execute_div = 0;
	output reg execute_divu = 0;
	output reg execute_j = 0;
	output reg execute_jal = 0;
	output reg execute_jalr = 0;
	output reg execute_jr = 0;
	output reg execute_mfhi = 0;
	output reg execute_mflo = 0;

	output wire[31:0] execute_imm_sx;
	output wire[31:0] execute_imm_zx;
	
	output reg[4:0] execute_rdid = 0;				//Destination register
	output reg execute_regwrite = 0;
	output reg[15:0] execute_imm = 0;
	
	//Jump address
	//Used for j and jal, not jr
	output reg[31:0] execute_jump_raw = 0;
	
	//Immediate shift amount
	output reg[4:0] execute_shamt = 0;
	
	//ALU operations are special
	output reg execute_rtype = 1;						//default nop is rtype
	output reg[5:0] execute_func = 0;
	
	output reg[4:0] execute_rsid = 0;
	output reg[4:0] execute_rtid = 0;
	
	output reg[5:0] execute_opcode = 0;
	
	output reg execute_bubble = 0;
	
	output reg execute_uses_rs = 0;					//Indicates if this instruction needs a valid, forwardable value
	output reg execute_uses_rt = 0;					//in the corresponding register
	
	output wire[4:0] decode_cp0_regid;
	
	input wire decode_mispredict;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Opcode table
	localparam OP_ADDI	= 6'b001000;
	localparam OP_ADDIU	= 6'b001001;
	localparam OP_ANDI	= 6'b001100;
	localparam OP_BEQ		= 6'b000100;
	localparam OP_BGTZ	= 6'b000111;
	localparam OP_BLEZ	= 6'b000110;
	localparam OP_BRANCH	= 6'b000001;	//BGEZ if rt =		00001
													//BGEZAL if rt =	10001
													//BLTZ if rt =		00000
													//BLTZAL if rt =	10000
	localparam OP_BNE		= 6'b000101;
	localparam OP_COPROC	= 4'b0100;
	localparam OP_J 		= 6'b000010;
	localparam OP_JAL		= 6'b000011;
	localparam OP_LB		= 6'b100000;
	localparam OP_LBU		= 6'b100100;
	localparam OP_LH		= 6'b100001;
	localparam OP_LHU		= 6'b100101;
	localparam OP_LW		= 6'b100011;
	localparam OP_LUI		= 6'b001111;
	localparam OP_ORI		= 6'b001101;
	localparam OP_RTYPE	= 6'b000000;
	localparam OP_SB		= 6'b101000;
	localparam OP_SH		= 6'b101001;
	localparam OP_SLTI	= 6'b001010;
	localparam OP_SLTIU	= 6'b001011;
	localparam OP_SW		= 6'b101011;
	localparam OP_XORI	= 6'b001110;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Function table for R-type instructions
	localparam FUNC_ADD  = 6'b100000;
	localparam FUNC_ADDU = 6'b100001;
	localparam FUNC_AND  = 6'b100100;
	localparam FUNC_DIV	= 6'b011010;
	localparam FUNC_DIVU	= 6'b011011;
	localparam FUNC_JR	= 6'b001000;
	localparam FUNC_JALR	= 6'b001001;		//Checked several sources and found two conflicting encodings.
														//This is the encoding used by gcc and the official MIPS manuals.
	localparam FUNC_MFHI = 6'b010000;
	localparam FUNC_MFLO = 6'b010010;
	localparam FUNC_MULT	= 6'b011000;
	localparam FUNC_MULTU= 6'b011001;
	localparam FUNC_NOR	= 6'b100111;
	localparam FUNC_OR   = 6'b100101;
	localparam FUNC_SLL	= 6'b000000;
	localparam FUNC_SLLV = 6'b000100;
	localparam FUNC_SLT	= 6'b101010;
	localparam FUNC_SLTU	= 6'b101011;
	localparam FUNC_SRA	= 6'b000011;
	localparam FUNC_SRL 	= 6'b000010;
	localparam FUNC_SRLV	= 6'b000110;
	localparam FUNC_SUB  = 6'b100010;
	localparam FUNC_SUBU = 6'b100011;
	localparam FUNC_XOR	= 6'b100110;
	
	localparam CP0_MF		= 6'b000000;
	localparam CP0_MT		= 6'b001000;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Main decode logic

	//Renaming of a few fields for easy use later on in the decode logic
	wire[31:0] decode_inst;
	assign decode_inst = ibus_din;
	wire[5:0] decode_opcode;
	assign decode_opcode = decode_inst[31:26];
	assign decode_rsid = decode_inst[25:21];
	assign decode_rtid = decode_inst[20:16];
	wire[4:0] decode_rdid;
	assign decode_rdid = decode_inst[15:11];
	wire[4:0] decode_shamt;
	assign decode_shamt = decode_inst[10:6];
	wire[15:0] decode_itype_imm;
	assign decode_itype_imm = decode_inst[15:0];
	wire[5:0] decode_func;
	assign decode_func = decode_inst[5:0];

	//Generic fields used by all instructions, or at least all of a specific type
	assign execute_imm_sx = { {16{execute_imm[15]}}, execute_imm };
	assign execute_imm_zx = { 16'h0, execute_imm };
	
	wire[31:0] decode_imm_sx;
	assign decode_imm_sx = { {16{decode_itype_imm[15]}}, decode_itype_imm };
		
	wire[31:0] decode_branch_addr;
	assign decode_branch_addr = decode_pc + {decode_imm_sx[29:0], 2'h0} + 4;
	
	wire[1:0] decode_coproc_num;
	assign decode_coproc_num = decode_opcode[1:0];
	
	wire[5:0] decode_coproc_opcode;
	assign decode_coproc_opcode = decode_inst[25:21];
	
	assign decode_cp0_regid = decode_rdid;
	
	//Sequential stuff for pipeline logic and cracking the instruction	
	always @(posedge clk) begin
	
		//Do NOT bubble out execute instruction in case of a jump!
		//This is necessary to properly execute the delay-slot instruction
		execute_bubble <= (decode_bubble | stall);
	
		//Default all one-hot enables to off
		//If we're bubbling, just don't set any of them! Saves us work with clock-enables during execution.
		execute_beq <= 0;
		execute_bne <= 0;
		execute_bgtz <= 0;
		execute_blez <= 0;
		execute_bgez <= 0;
		execute_bgezal <= 0;
		execute_bltz <= 0;
		execute_bltzal <= 0;
		execute_div <= 0;
		execute_divu <= 0;
		execute_j <= 0;
		execute_jal <= 0;
		execute_jalr <= 0;
		execute_jr <= 0;
		execute_mflo <= 0;
		execute_mfhi <= 0;
		
		execute_regwrite <= 0;
		
		execute_rtype <= 0;
		
		execute_uses_rs <= 0;
		execute_uses_rt <= 0;

		if(!hold) begin
		
			execute_opcode <= decode_opcode;
			
			//Bubble? Do nothing (but dont throw invalid instruction)
			//Treat a jump as a bubble
			if(decode_bubble || stall) begin
			end
			
			//If an instruction was fetched successfully, decode it and push down the pipeline
			else if(!decode_mispredict) begin
				`ifdef UTICA_TRACE_INSTR_FETCH
					$display("Fetched instruction: %x", decode_inst);
				`endif
					
				//R-type
				if(decode_opcode == OP_RTYPE) begin
					
					execute_rtype <= 1;
					execute_func <= decode_func;
					
					execute_rdid <= decode_rdid;
					execute_regwrite <= 1;
					execute_shamt <= decode_shamt;
					
					execute_uses_rs <= 1;
					execute_uses_rt <= 1;
					
					//Special handling for a handful of ops
					if( (decode_func == FUNC_DIV) || (decode_func == FUNC_DIVU) ||
						 (decode_func == FUNC_MULT) || (decode_func == FUNC_MULTU) ) begin
						execute_regwrite <= 0;
						
						if(decode_func == FUNC_DIV)
							execute_div <= 1;
						else if(decode_func == FUNC_DIVU)
							execute_divu <= 1;
					end
					else if(decode_func == FUNC_JALR) begin
						execute_jalr <= 1;
						execute_rdid <= 31;
						execute_uses_rt <= 0;
					end
					else if(decode_func == FUNC_JR) begin
						execute_jr <= 1;
						execute_regwrite <= 0;
						execute_uses_rt <= 0;
					end
					else if(decode_func == FUNC_MFLO) begin
						execute_mflo <= 1;
						execute_uses_rs <= 0;
						execute_uses_rt <= 0;
					end
					else if(decode_func == FUNC_MFHI) begin
						execute_mfhi <= 1;
						execute_uses_rs <= 0;
						execute_uses_rt <= 0;
					end
					
				end
				
				//J-type
				else if(decode_opcode == OP_J || decode_opcode == OP_JAL) begin
					
					if(decode_opcode == OP_J)
						execute_j <= 1;
					else begin
						execute_jal <= 1;
						execute_rdid <= 31;
						execute_regwrite <= 1;
					end
						
					//Jump address is the same in both cases
					execute_jump_raw <= {decode_pc[31:28], decode_inst[25:0], 2'h0 };
					
				end
				
				//Coprocessor
				else if(decode_opcode[5:2] == OP_COPROC) begin
					case(decode_coproc_num)
						0: begin
							case(decode_coproc_opcode)
								CP0_MF: begin
									//read cp0 reg
									//done combinatorially
								end
								
								CP0_MT: begin
									execute_uses_rt <= 1;
								end
							endcase
						end
						1:		$display("WARNING: Coproc 1 instructions not implemented");
						2:		$display("WARNING: Coproc 2 instructions not implemented");
						3:		$display("WARNING: Coproc 3 instructions not implemented");
					endcase					
				end
				
				//I-type
				else begin
				
					//Pull out common stuff
					execute_imm <= decode_itype_imm;
					
					//Default to writing register
					execute_rdid <= decode_rtid;
					execute_regwrite <= 1;
					
					//Default to using $rs only (rt is normally destination)
					execute_uses_rs <= 1;
					execute_uses_rt <= 0;
					
					//Don't write to register if branching
					if(decode_opcode == OP_BEQ || decode_opcode == OP_BGTZ || decode_opcode == OP_BLEZ ||
						(decode_opcode == OP_BRANCH && !decode_rtid[4]) || decode_opcode == OP_BNE || decode_opcode == OP_J ||
						decode_opcode == OP_SB || decode_opcode == OP_SH || decode_opcode == OP_SW) begin
						
						execute_rdid <= 0;
						execute_regwrite <= 0;
						
					end
					
					//Save branch target address
					if(decode_opcode == OP_BEQ || decode_opcode == OP_BNE || decode_opcode == OP_BGTZ || decode_opcode == OP_BLEZ ||
						decode_opcode == OP_BRANCH) begin
						execute_jump_raw <= decode_branch_addr;
						
						//use both regs
						execute_uses_rt <= 1;
					end
					
					//Mem operations have special rules
					if(decode_opcode == OP_SB ||
						decode_opcode == OP_SH ||
						decode_opcode == OP_SW) begin
						
						//use both regs
						execute_uses_rt <= 1;
						
					end
					
					//LUI doesn't use either reg
					if(decode_opcode == OP_LUI)
						execute_uses_rs <= 0;
						
					//SET stuff uses both
					if(decode_opcode == OP_SLTI || decode_opcode == OP_SLTIU)
						execute_uses_rt <= 1;
					
					//A handful of opcodes still need one-hot enables
					if(decode_opcode == OP_BEQ)
						execute_beq <= 1;
					if(decode_opcode == OP_BNE)
						execute_bne <= 1;
					if(decode_opcode == OP_BGTZ)
						execute_bgtz <= 1;
					if(decode_opcode == OP_BLEZ)
						execute_blez <= 1;
					if(decode_opcode == OP_BRANCH) begin
						execute_rdid <= 31;							//some of these instructoins link
						if(!decode_rtid[4] && decode_rtid[0])			//0xxx1 = BGEZ
							execute_bgez <= 1;
						else if(decode_rtid[4] && decode_rtid[0]) begin	//1xxx1 = BGEZAL
							execute_bgezal <= 1;
							execute_regwrite <= 1;
						end
						else if(!decode_rtid[4] && !decode_rtid[0])	//0xxx0 = BLTZ
							execute_bltz <= 1;
						else if(decode_rtid[4] && !decode_rtid[0]) begin	//1xxx0 = BLTZAL
							execute_bltzal <= 1;
							execute_regwrite <= 1;
						end
					end
					
				end
				
			end
				
			//Nope, cache miss or bad prediction. Nothing to do.
			else begin
				execute_rtype <= 0;
				execute_regwrite <= 0;
				execute_opcode <= 0;
				execute_func <= 0;
			end
		end
	end
	
	//Buffer rs, rt IDs for forwarding
	always @(posedge clk) begin
		if(!hold) begin
			execute_rsid <= decode_rsid;
			execute_rtid <= decode_rtid;
		end
	end

endmodule
